package controller;

import com.google.gson.Gson;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import local.AttemptFacadeLocal;
import local.IndustryFacadeLocal;
import local.ThemeFacadeLocal;
import model.Attempt;
import model.Industry;
import util.Chart;
import util.JsonReturnMsg;
import util.JsonSingleGraph;
import util.JsonStats;
import util.JsonTrend;

@WebServlet(name = "ChartServlet", urlPatterns = {"/ChartServlet"})
public class ChartServlet extends HttpServlet {

    @EJB
    AttemptFacadeLocal aFacade;
    @EJB
    IndustryFacadeLocal iFacade;
    @EJB
    ThemeFacadeLocal tFacade;
    Gson gson = new Gson();
    private String json;

    protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String action = request.getParameter("action");

        try {
            if (action.equals("getChartValue")) {
                getChartValue(request, response);
            } else if (action.equals("getIndustry")) {
                getIndustry(request, response);
            } else if (action.equals("getTrendData")) {
                getTrendData(request, response);
            } else if (action.equals("getStatsData")) {
                getStatsData(request, response);
            }

        } catch (Exception ex) {
            json = gson.toJson(new JsonReturnMsg("Error", ex.getMessage(), "error"));
            response.getWriter().println(json);
        } finally {
            response.getWriter().close();
        }
    }

    private void getIndustry(HttpServletRequest request, HttpServletResponse response) throws Exception {

        //industry
        LinkedHashMap<String, String> indList = iFacade.getAllIndustry();
        List indNameList = new ArrayList(indList.values());

        json = new Gson().toJson(indNameList);
        response.getWriter().println(json);
    }

    private void getChartValue(HttpServletRequest request, HttpServletResponse response) throws Exception {
        String content = request.getParameter("content");
        String industryID = iFacade.getIndustryID(request.getParameter("industryName"));

        int counter = 1;

        Chart c = new Chart();

        //categories and series
        LinkedHashMap<String, Double> scoreHM = tFacade.getThemeScoreTable(industryID, content);
        List scoreMV = new ArrayList(scoreHM.values());
        List scoreMVDesc = c.getListInDesc(scoreMV);

        List scoreMK = new ArrayList(scoreHM.keySet());
        List scoreMKDesc = c.getListInDesc(scoreMK);
        List rank = new ArrayList();

        LinkedHashMap<String, Double> avgScoreHM = tFacade.getThemeAvgScoreTable(industryID);
        List avgScores = new ArrayList();

        List themeNameList = new ArrayList();

        int totalAcceptableResponse = getTotalAcceptedResponse("", "", industryID);

        for (int i = 0; i < scoreMKDesc.size(); i++) {

            String themeIDStr = scoreMKDesc.get(i).toString();
            Long themeIDL = Long.parseLong(themeIDStr);

            //themeName
            themeNameList.add(tFacade.getTheme(themeIDL).getName());

            //avgScore
            double themeAvgScore = getThemeAvgScore(avgScoreHM, themeIDStr);
            avgScores.add(themeAvgScore);

            //rank
            if (i == 0) {
                rank.add(counter);
            } else {
                double valCurr = Double.parseDouble(scoreMVDesc.get(i).toString());
                double valPrev = Double.parseDouble(scoreMVDesc.get(i - 1).toString());

                if (valPrev > valCurr) {
                    counter++;
                }

                rank.add(counter);
            }
        }

        JsonSingleGraph sgj = new JsonSingleGraph();
        sgj.setAcceptableResponses(totalAcceptableResponse);
        sgj.setCategories(themeNameList);
        sgj.setSeries(avgScores);
        sgj.setRank(rank);
        sgj.setyAxis(scoreMVDesc);


        json = new Gson().toJson(sgj);
        response.getWriter().println(json);
    }

    private double getThemeAvgScore(LinkedHashMap<String, Double> avgScoreHM, String themeID) {

        List avgScoreMV = new ArrayList(avgScoreHM.values());
        List avgScoreMK = new ArrayList(avgScoreHM.keySet());

        for (int i = 0; i < avgScoreMK.size(); i++) {
            String currThemeID = (String) avgScoreMK.get(i);
            if (currThemeID.equals(themeID)) {
                double avgScore = Double.parseDouble(avgScoreMV.get(i).toString());
                return avgScore;
            }
        }

        return 0.00;

    }

    private void getStatsData(HttpServletRequest request, HttpServletResponse response) throws Exception {

        String exp_date_1 = request.getParameter("exp_date_1");
        String exp_date_2 = request.getParameter("exp_date_2");

        SimpleDateFormat sdf = new SimpleDateFormat(("dd/MM/yyyy"));
        Date date1 = sdf.parse(exp_date_1);
        Date date2 = sdf.parse(exp_date_2);

        if (date1.after(date2)) {
            json = gson.toJson(new JsonReturnMsg("Error", "Invalid Date Range", "error"));
            response.getWriter().println(json);
        } else {
            Industry[] industry = iFacade.getAllIndustryList();
            String[] industryName = new String[industry.length];
            int[] totalResponse = new int[industry.length];
            int[] totalCompletedResponse = new int[industry.length];
            int[] totalAcceptedResponse = new int[industry.length];


            for (int i = 0; i < industry.length; i++) {
                Long industryID = industry[i].getId();
                industryName[i] = industry[i].getName();
                totalResponse[i] = getTotalResponse(exp_date_1, exp_date_2, industryID.toString());
                totalAcceptedResponse[i] = getTotalAcceptedResponse(exp_date_1, exp_date_2, industryID.toString());
                totalCompletedResponse[i] = getTotalCompletedResponse(exp_date_1, exp_date_2, industryID.toString());
            }

            JsonStats sj = new JsonStats();
            sj.setIndustry(industryName);
            sj.setTotalResponse(totalResponse);
            sj.setTotalAcceptedResponse(totalAcceptedResponse);
            sj.setTotalCompletedResponse(totalCompletedResponse);

            json = new Gson().toJson(sj);
            response.getWriter().println(json);
        }
    }

    private int getTotalResponse(String exp_date_1, String exp_date_2, String industry) throws Exception {

        SimpleDateFormat sdf = new SimpleDateFormat(("dd/MM/yyyy"));

        String expDate1S = "01/01/1970";
        if (exp_date_1 != null) {
            if (!exp_date_1.equals("")) {
                expDate1S = exp_date_1;
            }
        }

        String expDate2S = "01/01/9999";
        if (exp_date_2 != null) {
            if (!exp_date_2.equals("")) {
                expDate2S = exp_date_2;
            }
        }

        Date expDate1 = sdf.parse(expDate1S);
        Date expDate2 = sdf.parse(expDate2S);

        Calendar c = Calendar.getInstance();
        c.setTime(expDate2);
        c.add(Calendar.DATE, 1);
        expDate2 = c.getTime();

        return aFacade.getTotalResponse(expDate1, expDate2, industry);
    }

    private int getTotalCompletedResponse(String exp_date_1, String exp_date_2, String industry) throws Exception {

        SimpleDateFormat sdf = new SimpleDateFormat(("dd/MM/yyyy"));

        String expDate1S = "01/01/1970";
        if (exp_date_1 != null) {
            if (!exp_date_1.equals("")) {
                expDate1S = exp_date_1;
            }
        }

        String expDate2S = "01/01/9999";
        if (exp_date_2 != null) {
            if (!exp_date_2.equals("")) {
                expDate2S = exp_date_2;
            }
        }

        Date expDate1 = sdf.parse(expDate1S);
        Date expDate2 = sdf.parse(expDate2S);

        Calendar c = Calendar.getInstance();
        c.setTime(expDate2);
        c.add(Calendar.DATE, 1);
        expDate2 = c.getTime();

        return aFacade.getTotalCompletedResponse(expDate1, expDate2, industry);
    }

    private int getTotalAcceptedResponse(String exp_date_1, String exp_date_2, String industry) throws Exception {

        SimpleDateFormat sdf = new SimpleDateFormat(("dd/MM/yyyy"));

        String expDate1S = "01/01/1970";
        if (exp_date_1 != null) {
            if (!exp_date_1.equals("")) {
                expDate1S = exp_date_1;
            }
        }

        String expDate2S = "01/01/9999";
        if (exp_date_2 != null) {
            if (!exp_date_2.equals("")) {
                expDate2S = exp_date_2;
            }
        }

        Date expDate1 = sdf.parse(expDate1S);
        Date expDate2 = sdf.parse(expDate2S);

        Calendar c = Calendar.getInstance();
        c.setTime(expDate2);
        c.add(Calendar.DATE, 1);
        expDate2 = c.getTime();

        return aFacade.getTotalAcceptedResponse(expDate1, expDate2, industry);
    }

    private void getTrendData(HttpServletRequest request, HttpServletResponse response) throws Exception {

        String exp_date_1 = request.getParameter("exp_date_1");
        String exp_date_2 = request.getParameter("exp_date_2");
        String timeline = request.getParameter("timeline");

        SimpleDateFormat sdf = new SimpleDateFormat(("dd/MM/yyyy"));
        Date date1 = sdf.parse(exp_date_1);
        Date date2 = sdf.parse(exp_date_2);

        if (date1.after(date2)) {
            json = gson.toJson(new JsonReturnMsg("Error", "Invalid Date Range", "error"));
            response.getWriter().println(json);
        } else {
            Industry[] industry = iFacade.getAllIndustryList();
            String[] industryName = new String[industry.length];

            List<Object[]> totalResponseList = new ArrayList<Object[]>();
            List<Object[]> totalAcceptedResponseList = new ArrayList<Object[]>();
            List<Object[]> totalCompletedResponseList = new ArrayList<Object[]>();
            List<Object[]> subjectList = new ArrayList<Object[]>();

            for (int i = 0; i < industry.length; i++) {
                Long industryID = industry[i].getId();
                industryName[i] = industry[i].getName();

                LinkedHashMap<String, Integer> totalResponse = getResponsesList(exp_date_1, exp_date_2, industryID.toString(), timeline, "TOTALRESPONSE");
                List tr = new ArrayList(totalResponse.values());
                List trKey = new ArrayList(totalResponse.keySet());
                totalResponseList.add(tr.toArray());
                subjectList.add(trKey.toArray());

                LinkedHashMap<String, Integer> totalAcceptedResponse = getResponsesList(exp_date_1, exp_date_2, industryID.toString(), timeline, "TOTALACCEPTEDRESPONSE");
                List tar = new ArrayList(totalAcceptedResponse.values());
                totalAcceptedResponseList.add(tar.toArray());

                LinkedHashMap<String, Integer> totalCompletedResponse = getResponsesList(exp_date_1, exp_date_2, industryID.toString(), timeline, "TOTALCOMPLETEDRESPONSE");
                List tcr = new ArrayList(totalCompletedResponse.values());
                totalCompletedResponseList.add(tcr.toArray());


            }


            JsonTrend tj = new JsonTrend();
            tj.setIndustry(industryName);
            tj.setSubjectList(subjectList);
            tj.setTotalResponseList(totalResponseList);
            tj.setTotalAcceptedResponseList(totalAcceptedResponseList);
            tj.setTotalCompletedResponseList(totalCompletedResponseList);

            json = new Gson().toJson(tj);
            response.getWriter().println(json);
        }


    }

    private LinkedHashMap<String, Integer> getResponsesList(String exp_date_1, String exp_date_2, String industry, String timeLine, String responseType) throws Exception {

        SimpleDateFormat sdf = new SimpleDateFormat(("dd/MM/yyyy"));

        String expDate1S = "01/01/1970";
        if (exp_date_1 != null) {
            if (!exp_date_1.equals("")) {
                expDate1S = exp_date_1;
            }
        }

        String expDate2S = "01/01/9999";
        if (exp_date_2 != null) {
            if (!exp_date_2.equals("")) {
                expDate2S = exp_date_2;
            }
        }

        Date expDate1 = sdf.parse(expDate1S);
        Date expDate2 = sdf.parse(expDate2S);

        Calendar c = Calendar.getInstance();
        c.setTime(expDate2);
        c.add(Calendar.DATE, 1);
        expDate2 = c.getTime();

        List<Attempt> attemptList = new ArrayList<Attempt>();

        if (responseType.equalsIgnoreCase("TOTALRESPONSE")) {
            attemptList = aFacade.getTotalResponseList(expDate1, expDate2, industry);
        } else if (responseType.equalsIgnoreCase("TOTALCOMPLETEDRESPONSE")) {
            attemptList = aFacade.getTotalCompletedResponseList(expDate1, expDate2, industry);
        } else if (responseType.equalsIgnoreCase("TOTALACCEPTEDRESPONSE")) {
            attemptList = aFacade.getTotalAcceptedResponseList(expDate1, expDate2, industry);
        }

        c.add(Calendar.DATE, -1);
        expDate2 = c.getTime();
        LinkedHashMap<String, Integer> resultList = new LinkedHashMap<String, Integer>();
        if (timeLine.equalsIgnoreCase("ANNUAL")) {
            resultList = getAnnualList(attemptList, expDate1, expDate2);
        } else if (timeLine.equalsIgnoreCase("BIANNUAL")) {
            resultList = getBiAnnualList(attemptList, expDate1, expDate2);
        } else if (timeLine.equalsIgnoreCase("MONTHLY")) {
            resultList = getMonthlyList(attemptList, expDate1, expDate2);
        } else if (timeLine.equalsIgnoreCase("QUARTER")) {
            resultList = getQuarterList(attemptList, expDate1, expDate2);
        }

        return resultList;

    }

    private LinkedHashMap<String, Integer> getQuarterList(List<Attempt> attemptList, Date expDate1, Date expDate2) throws Exception {
        //pre: sorted linkedhashmap order by timestamp (earliest to latest date)

        LinkedHashMap<String, Integer> acceptableAttemptHM = new LinkedHashMap<String, Integer>();

        //set the quarter of the year based on the input date
        Calendar c = Calendar.getInstance();
        c.setTime(expDate1);
        int year = c.get(Calendar.YEAR);
        int mth = c.get(Calendar.MONTH);
        int quarter = 1;

        if ((mth == 3) || (mth == 4) || (mth == 5))//april,may,june
        {
            quarter = 2;
        } else if ((mth == 6) || (mth == 7) || (mth == 8))//july,august,september
        {
            quarter = 3;
        } else if ((mth == 9) || (mth == 10) || (mth == 11))//october,novemnber,december
        {
            quarter = 4;
        }

        //creation of newDate (the next quarter). This is used for checking. 
        //i.e. if we want to get Q4 2010, we will check if the attempt's timestamp is before the newDate/next quarter date (Q12011, 01/01/2011)
        String newDateStr = "01/";

        if ((mth == 1) || (mth == 4) || (mth == 7) || (mth == 10)) //if is feb,may,aug,nov, add 2 mths instead of 3 mths.
        {
            c.add(Calendar.MONTH, 2);
        } else if ((mth == 2) || (mth == 5) || (mth == 8) || (mth == 11)) //if is mar,jun,sep,dec, add 1 mths instead of 3 mths.
        {
            c.add(Calendar.MONTH, 1);
        } else //if is jan,apr,jul,oct, add 3 mths to go to the next quarter.
        {
            c.add(Calendar.MONTH, 3);
        }

        int newDateMth = c.get(Calendar.MONTH);
        newDateMth = newDateMth + 1;//it march is 2 and april is 3 for Calendar Class...so +1
        if (newDateMth < 10) {
            newDateStr = newDateStr + "0" + newDateMth + "/" + c.get(Calendar.YEAR);
        } else {
            newDateStr = newDateStr + newDateMth + "/" + c.get(Calendar.YEAR);
        }

        SimpleDateFormat sdf = new SimpleDateFormat(("dd/MM/yyyy"));
        Date newDate = sdf.parse(newDateStr);
        c.setTime(newDate);


        Calendar c2 = Calendar.getInstance();
        c2.setTime(expDate2);
        int expDate2Mth = c2.get(Calendar.MONTH);

        String newDate2Str = "01/";

        if ((expDate2Mth == 1) || (expDate2Mth == 4) || (expDate2Mth == 7) || (expDate2Mth == 10)) //if is feb,may,aug,nov, add 2 mths instead of 3 mths.
        {
            c2.add(Calendar.MONTH, 2);
        } else if ((expDate2Mth == 2) || (expDate2Mth == 5) || (expDate2Mth == 8) || (expDate2Mth == 11)) //if is mar,jun,sep,dec, add 1 mths instead of 3 mths.
        {
            c2.add(Calendar.MONTH, 1);
        } else //if is jan,apr,jul,oct, add 3 mths to go to the next quarter.
        {
            c2.add(Calendar.MONTH, 3);
        }

        expDate2Mth = c2.get(Calendar.MONTH);
        expDate2Mth = expDate2Mth + 1;//it march is 2 and april is 3 for Calendar Class...so +1
        if (expDate2Mth < 10) {
            newDate2Str = newDate2Str + "0" + expDate2Mth + "/" + c2.get(Calendar.YEAR);
        } else {
            newDate2Str = newDate2Str + expDate2Mth + "/" + c2.get(Calendar.YEAR);
        }

        Date newDate2 = sdf.parse(newDate2Str);


        if (attemptList.size() <= 0) //if list is empty
        {
            Calendar c3 = Calendar.getInstance();
            c3.setTime(newDate);
            c3.add(Calendar.DATE, -1);
            acceptableAttemptHM.put("Q" + quarter + " " + c3.get(Calendar.YEAR), 0);

            boolean cont = true;

            while (cont) {
                c.add(Calendar.MONTH, 3);//add 3 months to the newDate to get the next quarter
                newDate = c.getTime();

                if (quarter == 4) {//if is last quarter, restart from quarter 1. Else, increase to the next quarter.
                    quarter = 1;
                } else {
                    quarter++;
                }

                if (newDate.after(newDate2)) {
                    cont = false;
                } else {
                    c3.setTime(newDate);
                    c3.add(Calendar.DATE, -1);
                    acceptableAttemptHM.put("Q" + quarter + " " + c3.get(Calendar.YEAR), 0);
                }
            }
        } else //if list is not empty
        {
            int counter = 0;

            for (int i = 0; i < attemptList.size(); i++) {
                Attempt a = (Attempt) attemptList.get(i);
                Date dte = a.getTimestamps();

                if (dte.before(newDate) && (i < (attemptList.size() - 1)))//if current attempt's timestamps is before newDate (the current quarter),can cont and increase the counter (Provided it is not the last item in the list).
                {
                    counter++;
                } else if (i >= (attemptList.size() - 1))//if it is last item in the list.                {
                {
                    Calendar c3 = Calendar.getInstance();
                    c3.setTime(newDate);
                    c3.add(Calendar.DATE, -1);

                    if (dte.before(newDate)) {
                        counter++;
                    }
                    acceptableAttemptHM.put("Q" + quarter + " " + c3.get(Calendar.YEAR), counter);

                    boolean cont = true;
                    while (cont) {
                        Date quarterStartDte = c.getTime(); //first day of the current quarter
                        c.add(Calendar.MONTH, 3); //first day of next quarter
                        newDate = c.getTime();
                        c3.add(Calendar.MONTH, 3); //last day of current quarter
                        counter = 0;

                        if (quarter == 4) {//if is last quarter, restart from quarter 1. Else, increase to the next quarter.
                            quarter = 1;
                        } else {
                            quarter++;
                        }

                        if (c3.getTime().before(newDate2)) {
                            if ((dte.after(quarterStartDte) || (dte.compareTo(quarterStartDte) == 0)) && dte.before(newDate)) {
                                counter++;
                                acceptableAttemptHM.put("Q" + quarter + " " + c3.get(Calendar.YEAR), counter);
                            } else {
                                acceptableAttemptHM.put("Q" + quarter + " " + c3.get(Calendar.YEAR), 0);
                            }

                        } else {
                            cont = false;
                        }
                    }
                    return acceptableAttemptHM;

                } else//the quarter have ended, add another 3 months (new quarter) to the newDate
                {

                    Calendar c3 = Calendar.getInstance();
                    c3.setTime(newDate);
                    c3.add(Calendar.DATE, -1);
                    acceptableAttemptHM.put("Q" + quarter + " " + c3.get(Calendar.YEAR), counter);

                    c.setTime(newDate);



                    boolean cont = true;
                    while (cont) {
                        c.add(Calendar.MONTH, 3);//add 3 months to the newDate to get the next quarter
                        newDate = c.getTime();
                        counter = 0;//reset counter to 0 for the new quarter

                        if (quarter == 4) {//if is last quarter, restart from quarter 1. Else, increase to the next quarter.
                            quarter = 1;
                        } else {
                            quarter++;
                        }

                        if (dte.before(newDate)) {
                            counter++;
                            cont = false;
                        } else {
                            c3.setTime(newDate);
                            c3.add(Calendar.DATE, -1);
                            acceptableAttemptHM.put("Q" + quarter + " " + c3.get(Calendar.YEAR), 0);
                        }
                    }
                }

            }
        }
        return acceptableAttemptHM;
    }

    private LinkedHashMap<String, Integer> getMonthlyList(List<Attempt> attemptList, Date expDate1, Date expDate2) throws Exception {
        //pre: sorted linkedhashmap order by timestamp (earliest to latest date)
        LinkedHashMap<String, Integer> acceptableAttemptHM = new LinkedHashMap<String, Integer>();

        Calendar c = Calendar.getInstance();
        c.setTime(expDate1);
        int year = c.get(Calendar.YEAR);
        int mth = c.get(Calendar.MONTH);

        Calendar cExpDate2 = Calendar.getInstance();
        cExpDate2.setTime(expDate2);
        cExpDate2.add(Calendar.MONTH, 1);
        int expDate2Year = cExpDate2.get(Calendar.YEAR);
        int expDate2Mth = cExpDate2.get(Calendar.MONTH);
        expDate2Mth = expDate2Mth + 1;//it march is 2 and april is 3 for Calendar Class...so +1
        String expDate2Str = "01/";
        if (expDate2Mth < 10) {
            expDate2Str = expDate2Str + "0" + expDate2Mth + "/" + expDate2Year;
        } else {
            expDate2Str = expDate2Str + expDate2Mth + "/" + expDate2Year;
        }

        SimpleDateFormat sdf = new SimpleDateFormat(("dd/MM/yyyy"));
        Date newExpDate2 = sdf.parse(expDate2Str);
        cExpDate2.setTime(newExpDate2);

        String[] monthNameList = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};

        if (attemptList.size() <= 0)//if list is empty
        {
            acceptableAttemptHM.put(monthNameList[mth] + " " + year, 0);

            boolean cont = true;
            int newExpDate2Year = cExpDate2.get(Calendar.YEAR);
            int newExpDate2Mth = cExpDate2.get(Calendar.MONTH);

            while (cont) {
                c.add(Calendar.MONTH, 1);//add 1 mth to expDate1
                year = c.get(Calendar.YEAR);
                mth = c.get(Calendar.MONTH);

                if ((newExpDate2Year == year) && (newExpDate2Mth == mth)) {
                    cont = false;
                } else {
                    acceptableAttemptHM.put(monthNameList[mth] + " " + year, 0);
                }
            }
        } else//if list is not empty
        {

            int counter = 0;
            for (int i = 0; i < attemptList.size(); i++) {
                Attempt a = (Attempt) attemptList.get(i);
                Date dte = a.getTimestamps();
                Calendar dteCal = Calendar.getInstance();
                dteCal.setTime(dte);
                int dteYear = dteCal.get(Calendar.YEAR);
                int dteMth = dteCal.get(Calendar.MONTH);

                if ((dteYear == year) && (dteMth == mth) && (i < (attemptList.size() - 1))) { //same year same mth, increment the counter
                    counter++;
                } else if (i >= (attemptList.size() - 1)) {//if last of list, add 0 for the rest of the mth till expDate2

                    if ((dteYear == year) && (dteMth == mth)) {
                        counter++;
                    }

                    acceptableAttemptHM.put(monthNameList[mth] + " " + year, counter);

                    boolean cont = true;
                    while (cont) {
                        c.add(Calendar.MONTH, 1);//add 1 mth to expDate1
                        counter = 0;
                        Date currNewDate = c.getTime();

                        if (currNewDate.before(newExpDate2)) {
                            year = c.get(Calendar.YEAR);
                            mth = c.get(Calendar.MONTH);
                            if ((dteYear == year) && (dteMth == mth)) //if reach the month and year, then stop and increase the countet
                            {
                                counter++;
                                acceptableAttemptHM.put(monthNameList[mth] + " " + year, counter);
                            } else {
                                acceptableAttemptHM.put(monthNameList[mth] + " " + year, 0);
                            }

                        } else {
                            cont = false;
                        }
                    }

                } else {//if yr and mth is diff, its indicate that the mth have end.
                    acceptableAttemptHM.put(monthNameList[mth] + " " + year, counter);//store the previous mths and its counter into hashmap
                    counter = 0; //reset counter for new mth.

                    boolean cont = true;
                    while (cont) {
                        c.add(Calendar.MONTH, 1);//add 1 mth to expDate1
                        year = c.get(Calendar.YEAR);
                        mth = c.get(Calendar.MONTH);

                        if ((dteYear == year) && (dteMth == mth)) //if reach the month and year, then stop and increase the countet
                        {
                            counter++;
                            cont = false;
                        } else //continue the while loop and search for the year and month that math..meanwhile add 0 for that month.
                        {
                            acceptableAttemptHM.put(monthNameList[mth] + " " + year, counter);
                        }
                    }

                }
            }
        }
        return acceptableAttemptHM;
    }

    private LinkedHashMap<String, Integer> getAnnualList(List<Attempt> attemptList, Date expDate1, Date expDate2) throws Exception {
        //pre: sorted linkedhashmap order by timestamp (earliest to latest date)
        LinkedHashMap<String, Integer> acceptableAttemptHM = new LinkedHashMap<String, Integer>();

        Calendar c = Calendar.getInstance();
        c.setTime(expDate1);
        int year = c.get(Calendar.YEAR);

        Calendar cExpDate2 = Calendar.getInstance();
        cExpDate2.setTime(expDate2);
        int expDate2Year = cExpDate2.get(Calendar.YEAR);

        if (attemptList.size() <= 0) {//if list is empty
            for (int i = year; i <= expDate2Year; i++) {
                acceptableAttemptHM.put("Year " + i, 0);
            }
        } else { //if list is not empty
            int counter = 0;
            for (int i = 0; i < attemptList.size(); i++) {
                Attempt a = (Attempt) attemptList.get(i);
                Date dte = a.getTimestamps();
                Calendar dteCal = Calendar.getInstance();
                dteCal.setTime(dte);
                int dteYear = dteCal.get(Calendar.YEAR);

                if ((dteYear == year) && (i < (attemptList.size() - 1))) { //same year, increment the counter
                    counter++;
                } else if (i >= (attemptList.size() - 1)) {//if same year but last of list, add 0 for the remaining year till expDate2
                    if (dteYear == year) {
                        counter++;
                    }
                    acceptableAttemptHM.put("Year " + year, counter);

                    boolean cont = true;
                    while (cont) {
                        c.add(Calendar.YEAR, 1);//add 1 year to expDate1
                        int currNewYear = c.get(Calendar.YEAR);
                        counter = 0;

                        if (currNewYear <= expDate2Year) {
                            year = c.get(Calendar.YEAR);
                            if (dteYear == year) {
                                counter++;
                                acceptableAttemptHM.put("Year " + year, counter);
                            } else {
                                acceptableAttemptHM.put("Year " + year, 0);
                            }

                        } else {
                            cont = false;
                        }
                    }

                } else {//if yr is diff, its indicate that the year have end.

                    acceptableAttemptHM.put("Year " + year, counter);//store the previous year and its counter into hashmap
                    counter = 0; //reset counter for the new year.

                    boolean cont = true;
                    while (cont) {
                        c.add(Calendar.YEAR, 1);//add 1 year to expDate1
                        year = c.get(Calendar.YEAR);

                        if (dteYear == year) //if reach the year, then stop and increase the counter
                        {
                            counter++;
                            cont = false;
                        } else //continue the while loop and search for the year that match..meanwhile add 0 for that month.
                        {
                            acceptableAttemptHM.put("Year " + year, counter);
                        }
                    }

                }
            }
        }

        return acceptableAttemptHM;
    }

    private LinkedHashMap<String, Integer> getBiAnnualList(List<Attempt> attemptList, Date expDate1, Date expDate2) throws Exception {
        //pre: sorted linkedhashmap order by timestamp (earliest to latest date)
        LinkedHashMap<String, Integer> acceptableAttemptHM = new LinkedHashMap<String, Integer>();

        //set the bi-annual of the year based on the input date
        Calendar c = Calendar.getInstance();
        c.setTime(expDate1);
        int year = c.get(Calendar.YEAR);
        int mth = c.get(Calendar.MONTH);
        String biAnnual = "1st Half of";

        if ((mth == 6) || (mth == 7) || (mth == 8) || (mth == 9) || (mth == 10) || (mth == 11))//jul,aug,sep,oct,nov,dec
        {
            biAnnual = "2nd Half of";
        }

        //creation of newDate (the next biAnnual). This is used for checking. 
        String newDateStr = "01/";

        if ((mth == 0) || (mth == 1) || (mth == 2) || (mth == 3) || (mth == 4) || (mth == 5))//jan,feb,mar,apr,may,jun
        {
            newDateStr = newDateStr + "07/" + year;
        } else if ((mth == 6) || (mth == 7) || (mth == 8) || (mth == 9) || (mth == 10) || (mth == 11))//jul,aug,sep,oct,nov,dec
        {
            newDateStr = newDateStr + "01/" + (year + 1);
        }

        SimpleDateFormat sdf = new SimpleDateFormat(("dd/MM/yyyy"));
        Date newDate = sdf.parse(newDateStr);
        c.setTime(newDate);


        Calendar c2 = Calendar.getInstance();
        c2.setTime(expDate2);
        int expDate2Year = c2.get(Calendar.YEAR);
        int expDate2Mth = c2.get(Calendar.MONTH);

        String newExpDate2Str = "01/";

        if ((expDate2Mth == 0) || (expDate2Mth == 1) || (expDate2Mth == 2) || (expDate2Mth == 3) || (expDate2Mth == 4) || (expDate2Mth == 5))//jan,feb,mar,apr,may,jun
        {
            newExpDate2Str = newExpDate2Str + "07/" + expDate2Year;
        } else if ((expDate2Mth == 6) || (expDate2Mth == 7) || (expDate2Mth == 8) || (expDate2Mth == 9) || (expDate2Mth == 10) || (expDate2Mth == 11))//jul,aug,sep,oct,nov,dec
        {
            newExpDate2Str = newExpDate2Str + "01/" + (expDate2Year + 1);
        }

        Date newExpDate2 = sdf.parse(newExpDate2Str);



        if (attemptList.size() <= 0) //if list is empty
        {
            Calendar c3 = Calendar.getInstance();
            c3.setTime(newDate);
            c3.add(Calendar.DATE, -1);
            acceptableAttemptHM.put(biAnnual + " " + c3.get(Calendar.YEAR), 0);

            c.setTime(newDate);
            boolean cont = true;

            while (cont) {
                c.add(Calendar.MONTH, 6);//add 6 months to the newDate to get the next biAnnual
                newDate = c.getTime();

                if (biAnnual.equalsIgnoreCase("1st Half of")) {
                    biAnnual = "2nd Half of";
                } else {
                    biAnnual = "1st Half of";
                }

                if (newDate.after(newExpDate2)) {
                    cont = false;
                } else {
                    c3.setTime(newDate);
                    c3.add(Calendar.DATE, -1);
                    acceptableAttemptHM.put(biAnnual + " " + c3.get(Calendar.YEAR), 0);
                }
            }
        } else //if list is not empty
        {
            int counter = 0;

            for (int i = 0; i < attemptList.size(); i++) {
                Attempt a = (Attempt) attemptList.get(i);
                Date dte = a.getTimestamps();

                if (dte.before(newDate) && (i < (attemptList.size() - 1)))//if current attempt's timestamps is before newDate (the current bi-Annual),can cont and increase the counter (Provided it is not the last item in the list).
                {
                    counter++;
                } else if (i >= (attemptList.size() - 1))//if current attempt's timestamps is before newDate (the current bi-Annual) but is last item in the list.                {
                {
                    Calendar c3 = Calendar.getInstance();
                    c3.setTime(newDate);
                    c3.add(Calendar.DATE, -1);

                    if (dte.before(newDate)) {
                        counter++;
                    }
                    acceptableAttemptHM.put(biAnnual + " " + c3.get(Calendar.YEAR), counter);

                    boolean cont = true;
                    while (cont) {
                        Date biAnnualStartDte = c.getTime();
                        c.add(Calendar.MONTH, 6);
                        newDate = c.getTime();
                        c3.add(Calendar.MONTH, 6);
                        counter = 0;
                        if (biAnnual.equalsIgnoreCase("1st Half of")) {
                            biAnnual = "2nd Half of";
                        } else {
                            biAnnual = "1st Half of";
                        }

                        if (c3.getTime().before(newExpDate2)) {
                            if ((dte.after(biAnnualStartDte) || (dte.compareTo(biAnnualStartDte) == 0)) && dte.before(newDate)) {
                                counter++;
                                acceptableAttemptHM.put(biAnnual + " " + c3.get(Calendar.YEAR), counter);
                            } else {
                                acceptableAttemptHM.put(biAnnual + " " + c3.get(Calendar.YEAR), 0);
                            }

                        } else {
                            cont = false;
                        }
                    }
                    return acceptableAttemptHM;

                } else//the bi-Annual have ended, add another 6 months (new bi-Annual) to the newDate
                {

                    Calendar c3 = Calendar.getInstance();
                    c3.setTime(newDate);
                    c3.add(Calendar.DATE, -1);
                    acceptableAttemptHM.put(biAnnual + " " + c3.get(Calendar.YEAR), counter);

                    c.setTime(newDate);



                    boolean cont = true;
                    while (cont) {
                        c.add(Calendar.MONTH, 6);//add 6 months to the newDate to get the next biAnnual
                        newDate = c.getTime();
                        counter = 0;//reset counter to 0 for the new bi-Annual


                        if (biAnnual.equalsIgnoreCase("1st Half of")) {
                            biAnnual = "2nd Half of";
                        } else {
                            biAnnual = "1st Half of";
                        }

                        if (dte.before(newDate)) {
                            counter++;
                            cont = false;
                        } else {
                            c3.setTime(newDate);
                            c3.add(Calendar.DATE, -1);
                            acceptableAttemptHM.put(biAnnual + " " + c3.get(Calendar.YEAR), 0);
                        }
                    }
                }

            }
        }
        return acceptableAttemptHM;
    }
    // <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">

    /** 
     * Handles the HTTP <code>GET</code> method.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /** 
     * Handles the HTTP <code>POST</code> method.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /** 
     * Returns a short description of the servlet.
     * @return a String containing servlet description
     */
    @Override
    public String getServletInfo() {
        return "Short description";
    }// </editor-fold>
}
